package github.saw96x.serialize.kryo;

/**
 * @author Saw96x
 * @date 2022/3/18 9:28
 */

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import github.saw96x.common.exception.SerializeException;
import github.saw96x.remoting.pojo.RpcRequest;
import github.saw96x.remoting.pojo.RpcResponse;
import github.saw96x.serialize.Serializer;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * Kryo serialization class, Kryo serialization efficiency is very high, but only compatible with Java language
 *
 * @author shuang.kou
 * @createTime 2020年05月13日 19:29:00
 */
@Slf4j
public class KryoSerializer implements Serializer {

  /**
   * Because Kryo is not thread safe. So, use ThreadLocal to store Kryo objects
   */
  private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
    Kryo kryo = new Kryo();
    kryo.register(RpcResponse.class);
    kryo.register(RpcRequest.class);
    return kryo;
  });

  @Override
  public byte[] serialize(Object obj) {
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         Output output = new Output(byteArrayOutputStream)) {
      Kryo kryo = kryoThreadLocal.get();
      // Object->byte:将对象序列化为byte数组
      kryo.writeObject(output, obj);
      kryoThreadLocal.remove();
      return output.toBytes();
    } catch (Exception e) {
      throw new SerializeException("Serialization failed");
    }
  }

  @Override
  public <T> T deserialize(byte[] bytes, Class<T> clazz) {
    try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
         Input input = new Input(byteArrayInputStream)) {
      Kryo kryo = kryoThreadLocal.get();
      // byte->Object:从byte数组中反序列化出对对象
      Object o = kryo.readObject(input, clazz);
      kryoThreadLocal.remove();
      return clazz.cast(o);
    } catch (Exception e) {
      throw new SerializeException("Deserialization failed");
    }
  }

}
